/*
 * $Id: utl_getmeminfo.c,v 1.2 2006-03-21 16:15:16 bacon Exp $
 */

#include <xp/sys/utl.h>

#if defined(_WIN32)
	#include <pdh.h>
#elif defined(__linux)
	#include <sys/sysinfo.h>
#elif defined(__FreeBSD__)
	#include <fcntl.h>
	#include <kvm.h>
	#include <paths.h>
	#include <unistd.h>
	#include <sys/sysctl.h>
#elif defined(sun)||defined(__sun)
	#include <kstat.h>
	#include <unistd.h>
	#include <sys/stat.h>
	#include <sys/swap.h>
#else
	#include <sys/sysctl.h>
#endif

/* TODO: implant an option to return the size in kbytes */
int xp_getmeminfo (xp_meminfo_t* mem)
{
#if defined(_WIN32) 
	#if (_WIN32_WINNT<0x0500)

	MEMORYSTATUS status;
	GlobalMemoryStatus(&status);
	mem->totalram = status.dwTotalPhys;
	mem->freeram = status.dwAvailPhys;
	mem->totalswap = status.dwTotalVirtual;
	mem->freeswap = status.dwAvailVirtual;

	#else

	MEMORYSTATUSEX status;
	if (GlobalMemoryStatusEx(&status) == FALSE) return -1;
	mem->totalram = status.ullTotalPhys;
	mem->freeram = status.ullAvailPhys;
	mem->totalswap = status.ullTotalVirtual;
	mem->freeswap = status.ullAvailVirtual;

	#endif

	mem->usedram = mem->totalram - mem->freeram;
	mem->usedswap = mem->totalswap - mem->freeswap;
	return 0;
#elif defined(__linux)

	struct sysinfo info;

	/* reading /proc/meminfo is a good alternative to calling sysinfo */

	if (sysinfo(&info) == -1) return -1;
#ifdef XP_HAVE_SYS_SYSINFO_MEM_UNIT
	mem->totalram = info.totalram * info.mem_unit;
	mem->freeram = info.freeram * info.mem_unit;
	mem->totalswap = info.totalswap * info.mem_unit;
	mem->freeswap = info.freeswap * info.mem_unit;
#else
	mem->totalram = info.totalram;
	mem->freeram = info.freeram;
	mem->totalswap = info.totalswap;
	mem->freeswap = info.freeswap;
#endif

	mem->usedram = mem->totalram - mem->freeram;
	mem->usedswap = mem->totalswap - mem->freeswap;

	return 0;

#elif defined(__FreeBSD__)
	kvm_t* kvm;
	struct kvm_swap swap;
	int pagesize;

	xp_size_t len;
	long tmp;

	/* kvm_open will fail for normal users */
	kvm = kvm_open (XP_NULL, _PATH_DEVNULL, XP_NULL, O_RDONLY, XP_NULL);
	if (kvm == XP_NULL) return -1;

	if (kvm_getswapinfo (kvm, &swap, 1, 0) == -1) return -1;

	pagesize = getpagesize();

	mem->totalram = 0;
	mem->usedram = 0;
	mem->totalswap = swap.ksw_total * pagesize;
	mem->usedswap = swap.ksw_used * pagesize;
	mem->freeswap = mem->totalswap - mem->usedswap;
	
	kvm_close (kvm);

	len = xp_sizeof(tmp);
	if (sysctlbyname("vm.stats.vm.v_free_count", 
		&tmp, &len, XP_NULL, 0) == -1) return -1;

	mem->freeram = (xp_ulong_t)tmp * (xp_ulong_t)pagesize;

	len = xp_sizeof(tmp);
	if (sysctlbyname("hw.availpages", /*"vm.stats.vm.v_page_count",*/
		&tmp, &len, XP_NULL, 0) == -1) return -1;
	mem->totalram = (xp_ulong_t)tmp * (xp_ulong_t)pagesize;
	mem->usedram = mem->totalram - mem->freeram;
	return 0;

#elif defined(sun)||defined(__sun)
	kstat_ctl_t* kc;
	kstat_t* ks;
	kid_t kid;
	kstat_named_t* tmp;

	int pagesize;
	unsigned long pagestotal;
	unsigned long pagesfree;
	struct anoninfo anon;

	pagesize = getpagesize();

	kc = kstat_open ();
	if (kc == XP_NULL) return -1;

	ks = kstat_lookup (kc, "unix", -1, "system_pages");
	if (ks == XP_NULL) 
	{
		kstat_close (kc);
		return -1;
	}

	kid = kstat_read (kc, ks, XP_NULL);
	if (kid == -1) 
	{
		kstat_close (kc);
		return -1;
	}

	tmp = kstat_data_lookup (ks, "pagestotal");
	if (tmp == XP_NULL) 
	{
		kstat_close (kc);
		return -1;
	}
	pagestotal = tmp->value.ul;

	tmp = kstat_data_lookup (ks, "pagesfree");
	if (tmp == XP_NULL) 
	{
		kstat_close (kc);
		return -1;
	}
	pagesfree = tmp->value.ul;
	kstat_close (kc);

	mem->totalram = (xp_ulong_t)pagestotal * pagesize;	
	mem->freeram = (xp_ulong_t)pagesfree * pagesize;
	mem->usedram = mem->totalram - mem->freeram;

	if (swapctl(SC_AINFO, &anon) == -1) return -1;

	mem->totalswap = (xp_ulong_t)anon.ani_max * pagesize;
	mem->freeswap = (xp_ulong_t)(anon.ani_max - anon.ani_resv) * pagesize;
	mem->usedswap = mem->totalswap - mem->freeswap;
	return 0;
#else

	return -1;
#endif
}
